# -*- coding: utf-8 -*-
#
# Tubemaster
#
# Copyright (C) 2009 Selim Ok <selimok@gmail.com>
# Copyright (C) 2007-2008 Siegfried-Angel Gevatter Pujals <siggi.gevatter@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

# A part of this code stolen from youtube-dl and PISI (see pardus.org.tr)

import os
import threading
import time
import urllib2
import math

import constants


class FetchError(Exception):
    def __init__(self,value):
        self.value = value

    def __str__(self):
        return str(self.value).decode("utf-8")

class Fetcher(threading.Thread):
    def __init__(self, url, destdir, filename):
        self.url = url
        self.destdir = destdir
        self.filename = filename
        self.path = os.path.join(self.destdir,self.filename)
        self.partial_filename = self.filename + '.part'
        self.partial_path = os.path.join(self.destdir,self.partial_filename)
        self.stream = None

        self.percent    = 0
        self.eta        = '--:--'
        self.speed      = '---b/s'
        self.stopFlag   = False
        self.error      = None
        threading.Thread.__init__(self)

    def raiseError(self,text):
        self.setError(text)
        raise FetchError(text)

    def setError(self,text):
        self.error = text

    def run(self):
        if self.url=='':
            self.raiseError('URL address cannot be empty!')

        if  self.filename=='' or self.destdir=='':
            self.raiseError('File name or destination directory cannot be empty!')

        if not os.access(self.destdir, os.W_OK):
            self.raiseError('Access denied to write to destination directory: "%s"' % (self.destdir))

        if os.path.exists(self.path) and not os.access(self.path, os.W_OK):
            self.raiseError('Access denied to destination file: "%s"' % (self.path))

        try:
            self.stream = open(self.partial_path, 'wb')
            self.runDownload(self.stream, self.url)
        except:
            pass

        if os.stat(self.partial_path).st_size == 0:
            os.remove(self.partial_path)
            self.raiseError('The video is not available for the selected quality parameter!')



    def runDownload(self, stream, url):
        request = urllib2.Request(url, None, constants.std_headers)
        data = urllib2.urlopen(request)
        data_len = data.info().get('Content-length', None)
        data_len_str = self.format_bytes(data_len)
        byte_counter = 0
        block_size = 1024
        start = time.time()
        while self.stopFlag == False:
            # Progress message
            self.percent = self.calc_percent(byte_counter, data_len)
            self.eta = self.calc_eta(start, time.time(), data_len, byte_counter)
            self.speed = self.calc_speed(start, time.time(), byte_counter)

            # Download and write
            before = time.time()
            data_block = data.read(block_size)
            after = time.time()
            data_block_len = len(data_block)
            if data_block_len == 0:
                break
            byte_counter += data_block_len
            self.stream.write(data_block)
            block_size = self.best_block_size(after - before, data_block_len)

            # Apply rate limit
            #self.slow_down(start, byte_counter)
        # Raise an exception if something goes wrong
        if data_len is not None and str(byte_counter) != data_len:
            self.raiseError('Download Aborted')


    def format_bytes(self,bytes):
        if bytes is None:
            return 'N/A'
        if bytes == 0:
            exponent = 0
        else:
            exponent = long(math.log(float(bytes), 1024.0))
        suffix = 'bkMGTPEZY'[exponent]
        converted = float(bytes) / float(1024**exponent)
        return '%.2f%s' % (converted, suffix)


    def calc_percent(self,byte_counter, data_len):
        if data_len is None:
            return 0.0
        return int(float(byte_counter) / float(data_len) * 100.0)


    def calc_eta(self, start, now, total, current):
        if total is None:
            return '--:--'
        dif = now - start
        if current == 0 or dif < 0.001: # One millisecond
            return '--:--'
        rate = float(current) / dif
        eta = long((float(total) - float(current)) / rate)
        (eta_mins, eta_secs) = divmod(eta, 60)
        if eta_mins > 99:
            return '--:--'
        return '%02d:%02d' % (eta_mins, eta_secs)


    def calc_speed(self,start, now, bytes):
        dif = now - start
        if bytes == 0 or dif < 0.001: # One millisecond
            return '%10s' % '---b/s'
        return '%10s' % ('%s/s' % self.format_bytes(float(bytes) / dif))


    def best_block_size(self, elapsed_time, bytes):
        new_min = max(bytes / 2.0, 1.0)
        new_max = min(max(bytes * 2.0, 1.0), 4194304) # Do not surpass 4 MB
        if elapsed_time < 0.001:
            return int(new_max)
        rate = bytes / elapsed_time
        if rate > new_max:
            return int(new_max)
        if rate < new_min:
            return int(new_min)
        return int(rate)

    def getPercent(self):
        return int(self.percent)

    def getSpeed(self):
        return self.speed

    def getEta(self):
        return self.eta

    def stop(self):
        self.stopFlag = True
